﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

namespace Neto.Db.SqlSugar;

/// <summary>
///     SqlSugarAop
/// </summary>
[SuppressSniffer]
public static class SqlSugarAop
{
    /// <summary>
    ///     初始化数据库AOP
    /// </summary>
    /// <param name="db"></param>
    public static void InitDbAop(this ISqlSugarClient db)
    {
        SqlSugarHelper.SqlSugarDbSetting.ConnectionConfigs.ForEach(config =>
        {
            ////配置
            var dbProvider = db.AsTenant().GetConnectionScope((string)config.ConfigId);
            // 设置AOP
            dbProvider.SetAop();
        });
    }

    /// <summary>
    ///     设置 sqlsugar config
    /// </summary>
    /// <param name="db"></param>
    /// <param name="configId"></param>
    public static void SetAopByConfigId(this ISqlSugarClient db, string configId = "Neto")
    {
        var dbProvider = db.AsTenant().GetConnectionScope(configId);
        dbProvider.SetAop();
    }

    /// <summary>
    ///     设置AOP 和日志
    /// </summary>
    /// <param name="dbProvider"></param>
    public static void SetAop(this ISqlSugarClient dbProvider)
    {
        var sqlSugarDbSetting = AppX.GetConfig<SqlSugarDbSettingOptions>();

        // 设置超时时间
        dbProvider.Ado.CommandTimeOut = sqlSugarDbSetting?.AdoCommandTimeOut ?? 60;

        //每次Sql执行前事件
        dbProvider.SqlSugar_OnLogExecuting();
        //SQL执行完
        dbProvider.SqlSugar_OnLogExecuted();
        //可以修改SQL和参数的值
        dbProvider.SqlSugar_OnExecutingChangeSql();
        //SQL报错
        dbProvider.SqlSugar_OnError();
        // 差异/审计日志功能
        dbProvider.SqlSugar_OnDiffLogEvent();
        //插入和更新 前
        dbProvider.SqlSugar_DataExecuting();
        // //插入和更新 后(5.1.4.159 添加了DataChangesExecuted，特殊情况才存在bug，修复了在用，没修复不用)
        // dbProvider.SqlSugar_DataChangesExecuted();

        //全局过滤器
        dbProvider.SqlSugar_Filter();
    }

    /// <summary>
    ///     可以修改SQL和参数的值
    /// </summary>
    /// <param name="dbProvider"></param>
    /// <returns></returns>
    public static void SqlSugar_OnExecutingChangeSql(this ISqlSugarClient dbProvider)
    {
        dbProvider.Aop.OnExecutingChangeSql = (sql, pars) =>
        {
            //AbcLog.Debug(sql,"SqlSugar\\修改SQL和参数的值");
            //sql=newsql
            //foreach(var p in pars) //修改
            return new KeyValuePair<string, SugarParameter[]>(sql, pars);
        };
    }

    /// <summary>
    ///     每次Sql执行前事件
    /// </summary>
    /// <param name="dbProvider"></param>
    public static void SqlSugar_OnLogExecuting(this ISqlSugarClient dbProvider)
    {
        dbProvider.Aop.OnLogExecuting = (sql, pars) =>
        {
            var sqlSugarDbSetting = AppX.GetConfig<SqlSugarDbSettingOptions>();
            //获取原生SQL推荐 5.1.4.63  性能OK
            var sqlStr = UtilMethods.GetNativeSql(sql, pars);
            //5.0.8.2 获取无参数化 SQL 影响性能只适合调试
            if (sqlSugarDbSetting.IsRecordNoParsExecSql)
                sqlStr += "\r\n=======无参数化SQL=======\r\n" +
                          UtilMethods.GetSqlString(dbProvider.CurrentConnectionConfig.DbType, sql, pars);

            if (sqlSugarDbSetting.IsRecordExecSql)
            {
                //是否是事件源存储器
                if (sqlStr.Contains("SysEventSourceStorer") || sqlStr.Contains("sys_event_source_storer"))
                {
                    //是否记录事件源存储器执行sql
                    if (sqlSugarDbSetting.IsRecordEventBusExecSql)
                        LogX.Debug($"\r\n执行SQL:{sqlStr}", "SqlSugar\\AOP\\每次Sql执行前事件");
                }
                else
                    LogX.Debug($"\r\n执行SQL:{sqlStr}", "SqlSugar\\AOP\\每次Sql执行前事件");
            }

            #region 控制台打印

            if (sqlSugarDbSetting.IsConsoleOutExecSql)
            {
                if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
                    Console.ForegroundColor = ConsoleColor.Green;
                if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) ||
                    sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
                    Console.ForegroundColor = ConsoleColor.White;
                if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase))
                    Console.ForegroundColor = ConsoleColor.Blue;
                Console.WriteLine("\r\n【" + DateTime.Now + "——执行SQL】\r\n" + sqlStr +
                                  "\r\n-----------------------------\r\n");
            }

            #endregion 控制台打印
        };
    }

    /// <summary>
    ///     SQL执行完
    /// </summary>
    /// <param name="dbProvider"></param>
    public static void SqlSugar_OnLogExecuted(this ISqlSugarClient dbProvider)
    {
        dbProvider.Aop.OnLogExecuted = (sql, pars) =>
        {
            //执行时间超过1秒
            if (dbProvider.Ado.SqlExecutionTime.TotalSeconds > 1)
            {
                //代码CS文件名
                var fileName = dbProvider.Ado.SqlStackTrace.FirstFileName;
                //代码行数
                var fileLine = dbProvider.Ado.SqlStackTrace.FirstLine;
                //方法名
                var firstMethodName = dbProvider.Ado.SqlStackTrace.FirstMethodName;
                //dbProvider.Ado.SqlStackTrace.MyStackTraceList[1].xxx 获取上层方法的信息

                LogX.Debug(
                    $"代码cs文件夹名：{fileName}\r\n 代码行数：{fileLine}\r\n 方法名：{firstMethodName}\r\nsql:{sql}\r\n参数：{pars.ToJson()}",
                    "SqlSugar\\AOP\\SQL执行时间超过1秒事件");
            }
        };
    }

    /// <summary>
    ///     差异/审计日志功能
    /// </summary>
    /// <param name="dbProvider"></param>
    public static void SqlSugar_OnDiffLogEvent(this ISqlSugarClient dbProvider)
    {
        //差异日志功能
        //Sql执行完后会进该事件，该事件可以拿到更改前记录和更改后记录，执行时间等参数（可以监控表变动）
        dbProvider.Aop.OnDiffLogEvent = diffLogModel =>
        {
            var sqlSugarDbSetting = AppX.GetConfig<SqlSugarDbSettingOptions>();
            if (sqlSugarDbSetting.EnableDiffLog == false) return;
            try
            {
                //调用
                foreach (var item in SqlSugarHelper.GetMethods(typeof(ISqlSugarDiffLogListener)))
                {
                    var instance = Activator.CreateInstance(item.Key);
                    item.Value.Invoke(instance, new object[] { dbProvider, diffLogModel });
                }
            }
            catch (Exception ex)
            {
                LogX.Error(ex, "SqlSugar\\AOP\\差异日志");
            }
        };
    }

    /// <summary>
    ///     SQL报错
    /// </summary>
    /// <param name="dbProvider"></param>
    public static void SqlSugar_OnError(this ISqlSugarClient dbProvider)
    {
        dbProvider.Aop.OnError = exp =>
        {
            try
            {
                #region 控制台打印

                Console.ForegroundColor = ConsoleColor.Red;
                //var pars = dbProvider.Utilities.SerializeObject(((SugarParameter[])exp.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
                var pars = "";
                Console.WriteLine("【" + DateTime.Now + "——错误SQL】\r\n" +
                                  UtilMethods.GetSqlString(dbProvider.CurrentConnectionConfig.DbType, exp.Sql,
                                      (SugarParameter[])exp.Parametres) + $"\r\n错误消息：{exp.Message}");

                #endregion 控制台打印

                //exp.sql 这样可以拿到错误SQL
                //5.0.8.2 获取无参数化 SQL
                //UtilMethods.GetSqlString(DbType.SqlServer,exp.sql,exp.parameters)

                var sqlStr = UtilMethods.GetSqlString(dbProvider.CurrentConnectionConfig.DbType, exp.Sql,
                    (SugarParameter[])exp.Parametres);

                LogX.Debug(
                    $"Source:{exp.Source}\r\nSQL：{sqlStr}\r\nStackTrace:{exp.StackTrace}\r\nInnerException:{exp.InnerException.ToStringEx()}",
                    "SqlSugar\\AOP\\OnError");
            }
            catch (Exception ex)
            {
                LogX.Error(ex, "SqlSugar\\AOP\\OnError");
            }
        };
    }

    /// <summary>
    ///     插入和更新过滤器 后
    /// </summary>
    /// <param name="dbProvider"></param>
    public static void SqlSugar_DataChangesExecuted(this ISqlSugarClient dbProvider)
    {
        dbProvider.Aop.DataChangesExecuted = (oldValue, entityInfo) =>
        {
            /*oldValue表示当前字段值 等同于下面写法*/
            //var value=entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue);

            try
            {
                if (!entityInfo.EntityColumnInfo.IsPrimarykey) return; //通过主键保证只进一次事件
                //获取主键的值
                //var value = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue);
                foreach (var item in SqlSugarHelper.GetMethods(typeof(ISqlSugarDataChangedListener)))
                    try
                    {
                        var instance = Activator.CreateInstance(item.Key);
                        item.Value.Invoke(instance, new object[] { dbProvider, entityInfo });
                    }
                    catch (Exception ex2)
                    {
                        LogX.Error(ex2, "SqlSugar\\AOP\\数据改变监听");
                    }
            }
            catch (Exception ex)
            {
                LogX.Error(ex, "SqlSugar\\AOP\\插入更新删除过滤器");
            }
        };
    }

    /// <summary>
    ///     插入和更新过滤器 后
    /// </summary>
    /// <param name="dbProvider"></param>
    public static void SqlSugar_DataChangesExecuted(ISqlSugarClient dbProvider, object oldValue,
        DataFilterModel entityInfo)
    {
        try
        {
            if (!entityInfo.EntityColumnInfo.IsPrimarykey) return; //通过主键保证只进一次事件


            //如果字典中不存在，则返回
            if (!SqlSugarEdclHelper.ContainsKey(entityInfo.EntityName)) return;

            //获取主键的值
            //var value = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue);

            // Task.Run(() =>
            // {
            foreach (var item in SqlSugarHelper.GetMethods(typeof(ISqlSugarDataChangedListener)))
                try
                {
                    var instance = Activator.CreateInstance(item.Key);
                    item.Value.Invoke(instance, new object[] { dbProvider, entityInfo });
                }
                catch (Exception ex2)
                {
                    LogX.Error(ex2, "SqlSugar\\AOP\\数据改变监听");
                }
            // });
        }
        catch (Exception ex)
        {
            LogX.Error(ex, "SqlSugar\\AOP\\插入更新删除过滤器");
        }
    }

    /// <summary>
    ///     插入和更新过滤器 前
    /// </summary>
    /// <param name="dbProvider"></param>
    public static void SqlSugar_DataExecuting(this ISqlSugarClient dbProvider)
    {
        dbProvider.Aop.DataExecuting = (oldValue, entityInfo) =>
        {
            /*oldValue表示当前字段值 等同于下面写法*/
            //var value=entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue);

            try
            {
                //这样每条记录就只执行一次
                // SqlSugarCustomerCacheCenter.DataFilter(entityInfo);
                //entityInfo 有字段所有参数

                //兼容其它ORM AOP写法
                //SqlSugar是通过每行记录每个值进行的细粒度AOP,如果一行数据只想进一次事件
                if (entityInfo.EntityColumnInfo.IsPrimarykey) //通过主键保证只进一次事件
                {
                    //登录的用户ID
                    var loginUserId = App.HttpContext.GetLoginUserId<long>();
                    //登录的用户账号
                    var loginUserAccount = App.HttpContext.GetLoginUserAccount();

                    #region inset生效

                    ////判断是否是自增，不是自增就使用雪花ID，必须是long类型
                    //if (entityInfo.EntityColumnInfo.IsPrimarykey == true && entityInfo.EntityColumnInfo.IsIdentity == false && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType.Name == nameof(System.Int64))
                    //{
                    //    var filedvalue = entityInfo.EntityValue.GetFieldValue<long>(entityInfo.EntityColumnInfo.DbColumnName);
                    //    if (filedvalue <= 0)
                    //    {
                    //        //SetFiledValue(entityInfo, "Id", SnowFlakeSingle.Instance.NextId());
                    //        SetFiledValue(entityInfo, entityInfo.EntityColumnInfo.DbColumnName, YitIdHelper.NextId());
                    //    }
                    //}

                    switch (entityInfo.OperationType)
                    {
                        case DataFilterType.InsertByObject:
                        {
                            {
                                var isManual = false;
                                //获取当前字段的值
                                var properyInfo = entityInfo.EntityValue.GetType().GetProperty("CreatorUserId");
                                if (properyInfo != null)
                                {
                                    var value1 = properyInfo.GetValue(entityInfo.EntityValue);
                                    if (value1 != null && value1.ToInt32() > 0)
                                        //跳过
                                        isManual = true; //手动赋值了，不再自动赋值
                                }

                                //获取到的登录用户ID大于0，且没有手动设置，才自动设置
                                if (loginUserId > 0 && isManual == false)
                                    SetFiledValue(entityInfo, "CreatorUserId", loginUserId);
                            }
                            {
                                var isManual = false;
                                //获取当前字段的值
                                var properyInfo = entityInfo.EntityValue.GetType().GetProperty("CreatorUserName");
                                if (properyInfo != null)
                                {
                                    var value1 = properyInfo.GetValue(entityInfo.EntityValue);
                                    if (value1 != null && !string.IsNullOrWhiteSpace(value1.ToString()))
                                        //跳过
                                        isManual = true; //手动赋值了，不再自动赋值
                                }

                                //获取到的登录账号不为空，且没有手动设置，才自动设置
                                if (!string.IsNullOrWhiteSpace(loginUserAccount) && isManual == false)
                                    SetFiledValue(entityInfo, "CreatorUserName", loginUserAccount);
                            }
                            {
                                var isManual = false;
                                //获取当前字段的值
                                var properyInfo = entityInfo.EntityValue.GetType().GetProperty("CreationTime");
                                if (properyInfo != null)
                                {
                                    var value1 = properyInfo.GetValue(entityInfo.EntityValue);
                                    if (value1 != null && DateTimeOffset.TryParse(value1.ToString(), out var dt))
                                        if (dt > DateTimeOffset.MinValue)
                                            //跳过
                                            isManual = true; //手动赋值了，不再自动赋值
                                }

                                //没有手动设置，才自动设置
                                if (isManual == false) SetFiledValue(entityInfo, "CreationTime", DateTimeOffset.Now);
                            }
                            break;
                        }
                        case DataFilterType.UpdateByObject:
                        {
                            //是否逻辑删除(假删除)
                            //if (entityInfo.PropertyName == "IsDeleted")
                            //{
                            var properyDate = entityInfo.EntityValue.GetType().GetProperty("IsDeleted");
                            //判断是否有 IsDeleted字段，如果有才执行下面代码，否则会报错
                            if (properyDate != null)
                            {
                                var isdeleted = properyDate.GetValue(entityInfo.EntityValue, null).To<bool>();
                                if (isdeleted)
                                {
                                    if (loginUserId > 0) SetFiledValue(entityInfo, "DeletedUserId", loginUserId);
                                    if (!string.IsNullOrWhiteSpace(loginUserAccount))
                                        SetFiledValue(entityInfo, "DeletedUserName", loginUserAccount);
                                    SetFiledValue(entityInfo, "DeletedTime", DateTimeOffset.Now);
                                }
                                else
                                    SetUpdateInfo(entityInfo);
                            }
                            else
                                SetUpdateInfo(entityInfo);

                            void SetUpdateInfo(DataFilterModel dataFilterModel)
                            {
                                if (loginUserId > 0) SetFiledValue(dataFilterModel, "UpdateUserId", loginUserId);
                                if (!string.IsNullOrWhiteSpace(loginUserAccount))
                                    SetFiledValue(dataFilterModel, "UpdateUserName", loginUserAccount);
                                SetFiledValue(dataFilterModel, "UpdateTime", DateTimeOffset.Now);
                            }

                            break;
                        }
                        case DataFilterType.DeleteByObject:
                        {
                            var properyDate = entityInfo.EntityValue.GetType().GetProperty("IsDeleted");
                            if (properyDate != null)
                            {
                                //判断是否假删除，如果是，则标记为删除就可以了
                                var isdeleted = properyDate.GetValue(entityInfo.EntityValue, null).To<bool>();
                                if (isdeleted)
                                {
                                    if (loginUserId > 0) SetFiledValue(entityInfo, "DeletedUserId", loginUserId);
                                    if (!string.IsNullOrWhiteSpace(loginUserAccount))
                                        SetFiledValue(entityInfo, "DeletedUserName", loginUserAccount);
                                    SetFiledValue(entityInfo, "DeletedTime", DateTimeOffset.Now);
                                }
                                //真删除，不用处理
                            }

                            //真删除，不用处理
                            break;
                        }
                    }

                    #endregion inset生效

                    // #region update生效
                    //
                    // #endregion update生效
                    //
                    // #region delete
                    //
                    // #endregion delete
                }

                //设置指定字段的值
                static void SetFiledValue(DataFilterModel entityInfo, string filedName, object value)
                {
                    var properyInfo = entityInfo.EntityValue.GetType().GetProperty(filedName);
                    //获取当前字段的值
                    //var value1 = properyInfo.GetValue(entityInfo.EntityValue);
                    if (properyInfo != null) properyInfo.SetValue(entityInfo.EntityValue, value);
                }

                //根据当前列修改另一列 可以么写
                //if(当前列逻辑==XXX)
                //var properyDate = entityInfo.EntityValue.GetType().GetProperty("Date");
                //if(properyDate!=null)
                //properyDate.SetValue(entityInfo.EntityValue,1);
            }
            catch (Exception ex)
            {
                LogX.Error(ex, "SqlSugar\\AOP\\插入更新删除过滤器");
            }

            //数据改变后执行
            SqlSugar_DataChangesExecuted(dbProvider, oldValue, entityInfo);
        };
    }

    /// <summary>
    ///     全局过滤器
    /// </summary>
    public static void SqlSugar_Filter(this ISqlSugarClient dbProvider)
    {
        try
        {
            //https://www.donet5.com/Home/Doc?typeId=1205

            var customerAops = SqlSugarHelper.GetMethods(typeof(ISqlSugarAopFilter));
            if (customerAops.Count > 0)
                //自定义全局 AOP 过滤器
                foreach (var item in customerAops)
                {
                    var instance = Activator.CreateInstance(item.Key);
                    item.Value.Invoke(instance, new object[] { dbProvider });
                }
            else
            {
                //过滤器写在这儿就行了
                dbProvider.QueryFilter.AddTableFilter<IDeleted<long>>(it => it.IsDeleted == false);

                var tenantId = AppX.TenantId;
                if (tenantId > 0)
                    dbProvider.QueryFilter.AddTableFilter<ITenantEntity<long>>(it => it.TenantId == tenantId);
            }
        }
        catch (Exception ex)
        {
            LogX.Error(ex, "SqlSugar\\AOP\\全局过滤器");
        }
    }
}